package com.jackstraw.jack_base.remote_command.pool.entity;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.jackstraw.jack_base.remote_command.pool.SessionConnectionPool;
import com.jackstraw.jack_base.util.IPUtil;
import com.jcraft.jsch.Session;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;


@Data
@Slf4j
@Primary
@Component
@ConfigurationProperties(prefix = "jack.session")
@EnableConfigurationProperties(SessiontConnectionConfig.class)
public class SessiontConnectionConfig extends GenericObjectPoolConfig<Session> {

    public final static String DEFAULT_CONNECT_NAME = "default";
    public final static String DEFAULT_IP = "localhost";
    public final static String DEFAULT_USER_NAME = "root";
    public final static Integer DEFAULT_PORT = 22;
    public final static Integer DEFAULT_TIMEOUT = 0;

    private Boolean enable = Boolean.TRUE;
    private String connectName;
    private String ip;
    private Integer port;
    private String userName;
    private String password;
    private String rootPassword;

    /**
     * Socket连接超时时长，单位毫秒
     */
    private Integer timeout;

    private Boolean flag = Boolean.TRUE;

    private Map<String, SessiontConnectionConfig> servicePropertiesMap = new LinkedHashMap<>();

    private Map<String, Map<String, String>> jackDictPropertiesMap = new LinkedHashMap<>();

    private Map<String, String> jackDictPropertiesPublicMap = new LinkedHashMap<>();


    public void resolveDistProperties() {
        this.servicePropertiesMap.keySet().stream().forEach(connName -> {
            connName = connName.endsWith("root")
                    ? connName.substring(0, connName.length() - "root".length()) : connName;
            Map<String, String> dictPropertiesMap = this.jackDictPropertiesMap.get(connName);
            if (ObjectUtils.isEmpty(dictPropertiesMap)) {
                dictPropertiesMap = new LinkedHashMap<>();
                this.jackDictPropertiesMap.put(connName, dictPropertiesMap);
            }
            Map<String, String> finalDictPropertiesMap = dictPropertiesMap;
            this.getJackDictPropertiesPublicMap().keySet().stream().forEach(key -> {
                if (!finalDictPropertiesMap.containsKey(key)) {
                    finalDictPropertiesMap.put(key, this.getJackDictPropertiesPublicMap().get(key));
                }
            });
        });
    }


    public Map<String, SessiontConnectionConfig> resolverProperties() {
        if (flag) {
            if (this.servicePropertiesMap.size() == 0) {
                if (StringUtils.isEmpty(this.connectName)) {
                    this.connectName = DEFAULT_CONNECT_NAME;
                }
                this.servicePropertiesMap.put(this.connectName, this);

            } else {
                this.servicePropertiesMap.forEach((key, value) -> {
                    SessiontConnectionConfig sessiontConnectionConfig = new SessiontConnectionConfig();
                    BeanUtil.copyProperties(this, sessiontConnectionConfig, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                    BeanUtil.copyProperties(value, sessiontConnectionConfig, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                    BeanUtil.copyProperties(sessiontConnectionConfig, value, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                    if (StringUtils.isEmpty(value.getConnectName())) {
                        log.warn("connName is null，set key:{}", key);
                        value.setConnectName(key);
                    }

                    if (!key.equals(value.getConnectName())) {
                        log.warn("this connName:{} not equals map key,set connName is key:{}", value.getConnectName(), key);
                        value.setConnectName(key);
                    }

                    if (StringUtils.isEmpty(value.getIp())) {
                        value.setIp(DEFAULT_IP);
                    }
                    if (ObjectUtils.isEmpty(value.getPort())) {
                        value.setPort(DEFAULT_PORT);
                    }
                    if (StringUtils.isEmpty(value.getUserName())) {
                        value.setUserName(DEFAULT_USER_NAME);
                    }
                    if (StringUtils.isEmpty(value.getRootPassword())) {
                        log.warn("连接名为：{}的root用户名密码为空", value.getConnectName());
                    }
                    if (DEFAULT_USER_NAME.equals(value.getUserName())) {
                        if (StringUtils.isEmpty(value.getPassword()) && !StringUtils.equals(value.getPassword(), value.getRootPassword())) {
                            log.warn("连接名称为:{}为root用户连接，其密码为空或密码与root密码不一致");
                            value.setPassword(value.getRootPassword());
                        }
                    }
                    if (StringUtils.isEmpty(value.getPassword())) {
                        log.error("连接名为：{}的密码为空", value.getConnectName());
                        throw new RuntimeException("连接名称为:" + value.getConnectName() + "的密码为空");
                    }
                    if (ObjectUtils.isEmpty(value.getTimeout())) {
                        value.setTimeout(DEFAULT_TIMEOUT);
                    }
                });
            }
        }
        this.resolveDistProperties();
        flag = Boolean.FALSE;
        return this.servicePropertiesMap;
    }

    public String getIpCode() {
        return String.valueOf(IPUtil.ipToInt(this.getIp()));
    }

    public static String getSessionPoolName(String connName) {
        return connName.concat(SessionConnectionPool.SESSIONCONNECTIONPOOL_BEANNAME);
    }

    public static String getSessionPoolNameByRoot(String connName) {
        return connName.concat(SessionConnectionPool.SESSIONCONNECTIONPOOL_ROOT_BEANNAME);
    }

    public static String getRootConnName(String connName) {
        return connName.endsWith(SessionConnectionPool.ROOT) ? connName : connName.concat(SessionConnectionPool.ROOT);
    }

    public static String getConnName(String connName) {
        return connName.endsWith(SessionConnectionPool.ROOT) ? connName.substring(0, connName.length() - SessionConnectionPool.ROOT.length()) : connName;
    }

    public static String getIpByConnName(String connName, Map<String, SessiontConnectionConfig> sessiontConnectionConfigMap) {
        SessiontConnectionConfig sessiontConnectionConfig = sessiontConnectionConfigMap.get(connName);
        if (ObjectUtils.isNotEmpty(sessiontConnectionConfig)) {
            return sessiontConnectionConfig.getIp();
        }
        return "";
    }

}
